#pragma once
#include <iostream>
#include <string.h>
#include <string>

#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define RECVSIZE 1024

static const socklen_t sockaddr_in_size = sizeof(sockaddr_in);
static const socklen_t sockddr_size = sizeof(sockaddr);

class UdpSocket{
  public:
    UdpSocket():socket_fd(-1){}
    bool CreateSocket(){
      socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if(socket_fd < 0){
        perror("socket");
        return false;
      }
      return true;
    }
    bool Bind(const std::string& ip, uint16_t port){
      sockaddr_in myaddr;
      myaddr.sin_family = AF_INET;
      myaddr.sin_addr.s_addr = inet_addr(ip.c_str());
      myaddr.sin_port = htons(port);
      int ret = bind(socket_fd, (sockaddr*)&myaddr, sockaddr_in_size);
      if(ret < 0){
        perror("bind");
        return false;
      }
      return true;
    }
    bool SendTo(const std::string& buf, const std::string& ip, uint16_t port){
      sockaddr_in myaddr;
      myaddr.sin_family = AF_INET;
      myaddr.sin_addr.s_addr = inet_addr(ip.c_str());
      myaddr.sin_port = htons(port);
      ssize_t send_size = sendto(socket_fd, buf.c_str(), buf.size(), 0, (sockaddr*)&myaddr, sockaddr_in_size);
      if(send_size < 0){
        perror("sendto");
        return false;
      }
      return true;
    }
    bool RecevFrom(std::string* buf, std::string* ip = nullptr, uint16_t* port = nullptr){
      char tmp[RECVSIZE] = {0};
      sockaddr_in src_addr;
      socklen_t addrlen = sockaddr_in_size;
      ssize_t recv_size = recvfrom(socket_fd, tmp, RECVSIZE - 1, 0, (sockaddr*)&src_addr, &addrlen);
      if(recv_size < 0){
        perror("recvfrom");
        return false;
      }
      buf->assign(tmp, recv_size);
      if(ip){
        *ip = inet_ntoa(src_addr.sin_addr);
      }
      if(port){
        *port = ntohs(src_addr.sin_port);
      }
      return true;
    }
    bool SendTo(const std::string& buf, sockaddr_in& addr){
      ssize_t send_size = sendto(socket_fd, buf.c_str(), buf.size(), 0, (sockaddr*)&addr, sockaddr_in_size);
      if(send_size < 0){
        perror("sendto");
        return false;
      }
      return true;
    }
    bool RecevFrom(std::string* buf, sockaddr_in* addr){
      char tmp[RECVSIZE] = {0};
      socklen_t addrlen = sockaddr_in_size;
      ssize_t recv_size = recvfrom(socket_fd, tmp, RECVSIZE - 1, 0, (sockaddr*)addr, &addrlen);
      if(recv_size < 0){
        perror("recvfrom");
        return false;
      }
      buf->assign(tmp, recv_size);
      return true;
    }
    void Close(){
      close(socket_fd);
      socket_fd = -1;
    }
  private:
    int socket_fd;
};
